package com.ruoyi.common.utils.canopen;

import java.io.*;
import java.net.Socket;

/**
 * CANopen协议工具类 (支持32位输出控制)
 * 精简实现，移除修改模块ID功能，专注于32位输入输出控制
 */
public class CanopenProtocolUtil {
    private final String deviceIp;
    private final int devicePort;
    private final byte partitionCode;
    private static final int TIMEOUT = 3000;
    private static final int READ_WAIT_INTERVAL = 50;
    private static final int VALID_RESPONSE_LENGTH = 10;
    private static final int RETURN_VALUE_START_INDEX = 6;
    private static final int RETURN_VALUE_LENGTH = 4; // 32位=4字节
    private static final int TOTAL_BITS = 32;

    // 指令前缀(第一位分区码运行时替换)
    private static final byte[] READ_INPUT_PREFIX = {0x00, 0x00, 0x43, 0x01, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00};
    private static final byte[] CONTROL_OUTPUT_PREFIX = {0x00, 0x00, 0x23, 0x01, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00};

    // 构造函数
    public CanopenProtocolUtil(String deviceIp, int devicePort) {
        this(deviceIp, devicePort, (byte) 0x06);
    }

    public CanopenProtocolUtil(String deviceIp, int devicePort, byte partitionCode) {
        this.deviceIp = deviceIp;
        this.devicePort = devicePort;
        this.partitionCode = partitionCode;
    }

    /**
     * 读取输入状态(返回32位字符组)
     */
    public InputStatusResult readInputStatus(int deviceId) {
        CanopenResponse response = executeCommand(new CanopenCommand(CommandType.READ_INPUT, deviceId, null));

        byte[] returnValues = new byte[RETURN_VALUE_LENGTH];
        char[] total32BitChars = new char[TOTAL_BITS];
        String errorMessage = response.isSuccess() ? null : response.getErrorMessage();

        if (response.isSuccess()) {
            try {
                System.arraycopy(response.getResponseData(), RETURN_VALUE_START_INDEX, returnValues, 0, RETURN_VALUE_LENGTH);

                // 转换为32位字符组(第七到第十位顺序)
                for (int i = 0; i < RETURN_VALUE_LENGTH; i++) {
                    for (int j = 0; j < 8; j++) {
                        total32BitChars[i * 8 + j] = ((returnValues[i] >> j) & 0x01) == 0x01 ? '1' : '0';
                    }
                }
            } catch (Exception e) {
                errorMessage = "解析错误: " + e.getMessage();
            }
        }

        return new InputStatusResult(response, returnValues, total32BitChars, errorMessage);
    }

    /**
     * 控制32位输出(第七到第十位)
     * @param deviceId 设备ID
     * @param outputBits 32位输出状态数组(true=1, false=0)
     */
    public CanopenResponse controlOutput(int deviceId, boolean[] outputBits) {
        if (outputBits == null || outputBits.length != TOTAL_BITS) {
            throw new IllegalArgumentException("输出数组必须包含32个元素");
        }

        // 转换32位布尔数组为4字节
        byte[] outputBytes = new byte[RETURN_VALUE_LENGTH];
        for (int i = 0; i < RETURN_VALUE_LENGTH; i++) {
            for (int j = 0; j < 8; j++) {
                if (outputBits[i * 8 + j]) {
                    outputBytes[i] |= (1 << j);
                }
            }
        }

        return executeCommand(new CanopenCommand(CommandType.CONTROL_OUTPUT, deviceId, outputBytes));
    }

    // 执行单条命令
    private CanopenResponse executeCommand(CanopenCommand command) {
        try (Socket socket = new Socket(deviceIp, devicePort)) {
            socket.setSoTimeout(TIMEOUT);
            OutputStream out = socket.getOutputStream();
            InputStream in = socket.getInputStream();

            // 构建并发送命令
            byte[] request = buildCommandData(command);
            out.write(request);
            out.flush();

            // 接收响应
            ByteArrayOutputStream responseBuffer = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            long startTime = System.currentTimeMillis();
            boolean isReadCmd = command.getType() == CommandType.READ_INPUT;
            long maxWait = isReadCmd ? TIMEOUT * 2 : TIMEOUT;

            while (System.currentTimeMillis() - startTime < maxWait) {
                if (in.available() > 0) {
                    int bytesRead = in.read(buffer);
                    if (bytesRead > 0) {
                        responseBuffer.write(buffer, 0, bytesRead);
                        if (containsValidResponse(responseBuffer.toByteArray())) {
                            if (isReadCmd) Thread.sleep(READ_WAIT_INTERVAL);
                            break;
                        }
                    }
                } else {
                    Thread.sleep(READ_WAIT_INTERVAL);
                }
            }

            byte[] rawData = responseBuffer.toByteArray();
            if (rawData.length == 0) {
                return new CanopenResponse(command, false, null, "3秒超时未收到响应");
            }

            byte[] validData = extractValidResponse(rawData);
            if (validData == null) {
                return new CanopenResponse(command, false, null, "未找到有效响应");
            }

            return new CanopenResponse(command, isValidResponse(validData, command), validData, null);
        } catch (Exception e) {
            return new CanopenResponse(command, false, null, e.getMessage());
        }
    }

    // 构建命令数据
    private byte[] buildCommandData(CanopenCommand command) {
        byte[] cmd = new byte[10];
        int deviceId = command.getDeviceId();

        // 根据命令类型选择前缀
        switch (command.getType()) {
            case READ_INPUT:
                cmd = READ_INPUT_PREFIX.clone();
                break;
            case CONTROL_OUTPUT:
                cmd = CONTROL_OUTPUT_PREFIX.clone();
                if (command.getData() != null) {
                    // 设置第七到第十位(索引6-9)的32位数据
                    System.arraycopy(command.getData(), 0, cmd, 6, Math.min(4, command.getData().length));
                }
                break;
        }

        cmd[0] = partitionCode; // 设置分区码
        cmd[1] = cmd[5] = (byte) deviceId; // 设置设备ID
        return cmd;
    }

    // 提取有效响应
    private byte[] extractValidResponse(byte[] rawData) {
        if (rawData.length < VALID_RESPONSE_LENGTH) return null;
        for (int i = 0; i <= rawData.length - VALID_RESPONSE_LENGTH; i++) {
            if (rawData[i] == 0x05) {
                byte[] valid = new byte[VALID_RESPONSE_LENGTH];
                System.arraycopy(rawData, i, valid, 0, VALID_RESPONSE_LENGTH);
                return valid;
            }
        }
        return null;
    }

    // 检查是否包含有效响应
    private boolean containsValidResponse(byte[] response) {
        if (response.length < VALID_RESPONSE_LENGTH) return false;
        for (int i = 0; i <= response.length - VALID_RESPONSE_LENGTH; i++) {
            if (response[i] == 0x05) return true;
        }
        return false;
    }

    // 验证响应有效性
    private boolean isValidResponse(byte[] response, CanopenCommand command) {
        if (response.length != VALID_RESPONSE_LENGTH || response[0] != 0x05) return false;
        byte[] request = buildCommandData(command);

        return command.getType() == CommandType.READ_INPUT
                ? response[2] == request[2] && response[3] == request[3] && response[4] == request[4] && response[5] == request[5]
                : response[5] == request[5];
    }

    // 工具方法：字节数组转十六进制
    public static String bytesToHex(byte[] bytes) {
        if (bytes == null) return "null";
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) sb.append(String.format("%02X ", b));
        return sb.toString().trim();
    }

    /**
     * 输入状态结果封装类
     */
    public static class InputStatusResult {
        private final CanopenResponse response;
        private final byte[] returnValues; // 第七到第十位字节值
        private final char[] total32BitChars; // 32位字符组
        private final String errorMessage;

        public InputStatusResult(CanopenResponse response, byte[] returnValues, char[] total32BitChars, String errorMessage) {
            this.response = response;
            this.returnValues = returnValues;
            this.total32BitChars = total32BitChars;
            this.errorMessage = errorMessage;
        }

        public CanopenResponse getResponse() { return response; }
        public byte[] getReturnValues() { return returnValues; }
        public char[] getTotal32BitChars() { return total32BitChars; }
        public String getErrorMessage() { return errorMessage; }
        public boolean isSuccess() { return response != null && response.isSuccess() && errorMessage == null; }
    }
}

// 命令类型枚举
enum CommandType {
    READ_INPUT, CONTROL_OUTPUT
}

// 命令对象
class CanopenCommand {
    private final CommandType type;
    private final int deviceId;
    private final byte[] data;

    public CanopenCommand(CommandType type, int deviceId, byte[] data) {
        this.type = type;
        this.deviceId = deviceId;
        this.data = data;
    }

    public CommandType getType() { return type; }
    public int getDeviceId() { return deviceId; }
    public byte[] getData() { return data; }
}

// 测试类
class CanopenTest {
    public static void main(String[] args) {
        CanopenProtocolUtil canopen = new CanopenProtocolUtil("192.168.0.7", 20108);

        // 测试32位输出控制
        boolean[] output32Bits = new boolean[32];
        // 设置第32位(索引31)为1，其余为0 (示例)
        for (int i = 0; i < 32; i++) {
            output32Bits[i] = (i == 1 || i == 2);
        }

        try {
            // 发送32位输出控制命令
            CanopenResponse response = canopen.controlOutput(1, output32Bits);
            System.out.println("输出控制" + (response.isSuccess() ? "成功" : "失败: " + response.getErrorMessage()));

            // 读取控制结果
            CanopenProtocolUtil.InputStatusResult result = canopen.readInputStatus(1);
            if (result.isSuccess()) {
                System.out.println("32位输出结果: " + new String(result.getTotal32BitChars()));
                System.out.println("对应字节值: " + CanopenProtocolUtil.bytesToHex(result.getReturnValues()));
            }
        } catch (Exception e) {
            e.fillInStackTrace();
        }
    }
}
